/* 
// 仅支持es5
npm install uglify-js -g
// 同时支持 es5和6
npm install uglify-es -g


// uglifyjs xxx.js -m -c -o xxx.min.js   


-c  compress / 压缩
-m  mangle   / 混淆
-o  output   / 指定输出文件名 

在node中 

const UglifyJS = require(‘uglify-es’);
let options = { 
      compress: true, // 启用压缩 
      mangle: true, // 启用混淆 
      output: { 
          comments: false // 移除注释 
        } 
    };
*/

(function(){
'use strict'
var obk=Object.defineProperty;
var doc=document,bod=doc.body;

/**
 * 匹配类型
 * 
 * type matching
 */
var kType=Object.create(null);
    kType.string=1;
    kType.boolean=1;
    kType.number=1;

/**
 * Used to match {{}}
 */   
var BD0=Object.create(null);
    BD0['{']=1,BD0['}']=2;
/**
 * 用于匹配字符串的边界
 * 
 * Boundaries used to match strings
 */    
var BD1=Object.create(null);
    BD1["'"]=1,BD1['"']=2,BD1['`']=3;

/**
 * 用于匹配 各种运算符
 * 
 * Used to match commonly used operators
 * For example: +-*?/><=%:&()[]!
 */    
var BD2=Object.create(null);
    BD2['+']=1,BD2['-']=1,BD2['*']=1,BD2['\\']=1,BD2['%']=1,BD2['?']=1,BD2['>']=1,BD2['<']=1,BD2['=']=1,BD2['&']=1,BD2[':']=1,BD2[',']=1,BD2['!']=1;
    BD2['(']=2,BD2[')']=1;     // 2  方法 () 
    BD2['[']=3,BD2[']']=4;     // 3  data [] 的方法

/**
 * 用于转义 
 * 
 * Used to escape : $_index , $_value , $fork , $
 */    
var KT=Object.create(null);
    KT['$_value']='$O.$N(0)',KT['$_index']='$O.$N(1)',KT['$fork']='$data',KT['$']='$data',
    KT['true']='true',KT['false']='false',KT['null']='null',KT['undefind']='undefind',KT['NaN']='NaN';

/**
 * 获取数据的类型
 * Determine the type of data obtained
 * @param {*} v            any Data
 * @returns {Number}       0:disabled   1: basic( string , number , undefind , null ,boolean )  2: json  3: array 
 */
var typeLX =function(v){
    let u;
        if(v===u||v===null)return 1;
    let lx=typeof(v)
        if(kType[lx]===1)return 1;
        if(lx==='object'){
              if(v instanceof Array)return 3;                             // array  
              if(v.$type==='array')return 3;                              // array   
              if(v.constructor&&v.constructor.name ==='Object')return 2;  // json    (通过Object.create(null)创建的对象 无法判定!! )
              if(v.constructor===v.toString)return 2;                     // json    Object.create(null) 创建的json 没有这两个属性
        };
        return 0;
};

/**
 * 支持响应的 array 事件列表
 * 
 * Array event list of supported responses 
 */
var arrList=['push','unshift','pop','shift','splice','sort','reverse'];  

/**
 * 使得 array的部分事件 支持数据响应  , 'swap' 方法是用于交换两个 '已有' 数组元素位置的补充方法 
 * 
 * Enable some 'array' events to support data responses.The 'swap' method is a supplementary method used to exchange the positions of two existing elements in an array.
 * 
 * For example: arr=['a','b']  ;  arr.swap( 0, 1 ) ;   //arr=['b','a']
 * 
 * @param {Array} arr 
 * @param {Eng}   app   Eng instance  
 * @param {fork}  fork      
 */
var ArrEvt=function(arr,app,fork,path){
   let d=arr;
       if(d.$type==='array')return;
   let yx=Array.prototype;
       arrList.forEach((v)=>{
             var yxF=yx[v];  //原型方法
                d[v]=function(){
                    var t=this,m=arguments;
                         if(v==='sort'||v==='reverse'){
                            var vd=t.slice(0);
                                app.forUnBind(t)
                                yxF.apply(vd,m);
                         };
                        return app.forCase(v,m,t,fork,path,vd)
                        
                };
                obk(d,v,{enumerable:false,writable:false,configurable:false});
       });

      d.swap=function(){
           return app.forCase('swap',arguments,d,fork,path)
      };
       d.$type='array';
       obk(d,'$type',{enumerable:false,writable:false,configurable:false});
       obk(d,'swap',{enumerable:false,writable:false,configurable:false});            
};

/**
 * 使不可枚举 ,但可读写和删除 
 * 
 * Make it non enumerable, but readable, writable, and deleted
 * @param {*} o 
 * @param {*} k 
 */
var NoEnum=function(o,k){
    obk(o,k,{enumerable:false,writable:true,configurable:true}); 
};

/**
 * 使不可配置,不可删除
 * 
 * Make it non removable and non configurable
 * @param {*} o 
 * @param {*} k 
 */
var NoCfg=function(o,k){
    obk(o,k,{writable:false,configurable:false}); 
};

var NoDel=function(o,k){
    obk(o,k,{writable:true,configurable:false}); 
};
/* 
私有属性  禁止枚举  访问 
var stop={
    $data:true,
    $p:true,
    $g:true,
    $type:true,
    $pDom:true,    // 根 循环数据的 父元素
    $struct:true,
    $N:true,       // 数组的 索引    
    $V:true,       // 数组的 索引值  (用于基本类型的输出)
    $ARR:true,     // [] 储存所有 $_index 和 $_value 处理方法
    $JX:[],        // 镜像fork  同一份列表数据 多处渲染
    //
    $froot:true,   // 用于渲染的根数据  
    $cFor:true,    // 子循环数据
    // $clone 及其独有属性 
    $clone:true,   // 
    $doms:true     // [] 储存所有创建的dom 按照   $JX分支创建 
    $def:true      // [] 储存 默认 响应事件 
}
  禁止枚举列表  
*/

var noList=['$evt','$attr','$style','$show','$input','$change','$html','$txt','$text',    //  数据 处理存储对象 
             //  逻辑数据对象 
            '$data','$p','$g','$type','$N','$V','$ARR','$cFor',      //$N 和 $V 可以精简为一个 !!!!!!( 后续 修改 )
            '$cDom','$JX','$doms','$filter','$cmt',//'$def', '$pDom', '$clone','$struct'   
];

/**
 * 获取不可空对象 , 提前设置好不可枚举的对象  
 * @returns 
 */
var getEmpty=function(){
    var o=Object.create(null);
    var d=noList,i=0,l=d.length
        while(i<l){
            NoEnum(o,d[i])
            i++;
        };
        o.$JX=[];
    return o;
};

/**
 *  返回 数据 key 的待绑定类型
 * @param {*} k 
 * @returns   0:禁用 1:正常数据 2:不可枚举(但仍然向下响应)
 */
var keyType=function(k){
     if(k.indexOf('$$_')===0)return 0;   // $$_  不处理的数据
     if(k.indexOf('$_')===0)return 2;    // $_   不可枚举数据
     if(k.indexOf('$')===0)return 0;     // 不处理的 非法 数据  
     return 1;                           // 基本数据
};

/**
 * 根据字符串生成返回一个dom   (用于dom字符串 模板)
 * @param {*} str 
 */
var getDom=function(str){
  var a=doc.createElement('div');
      a.innerHTML=str;
      return a.children[0];
};

/**
 * 从父元素中删除dom 
 * @param {*} dom 
 * @returns 
 */
var delDom=function(dom){
    if(!dom)return;
var p=dom.parentNode;
     if(p)p.removeChild(dom);  
};  

/**
 * 获取每个数据'fork'的子分支'fork'
 * 
 * Obtain the sub branch 'fork' of each data 'fork'
 * @param {fork} parent    parent fork
 * @param {fork} global    root fork 
 * @returns {fork}
 */
var getFork=function(parent,global){
    let o=getEmpty(),i=0,l=atrList.length,k='';
        //
        o.$p=parent||o;
        o.$g=global||o;
        while(i<l){
            k=atrList[i];
            o[k]=[];
            i++;
        };
        o.$type='base';
        return o
};

var regWrap=/(\r|\n)/g;
/**
 * 行内表达式的 取值方式 
 * @param {*} str 
 * @returns 
 */
var getVfun=function(str){
          var fun=null;
               try {
                  fun=Function('"use strict";return (' + str.replace(regWrap,'') + ')')();
               } catch (err) {
                  console.error(str);
                  console.error(err)
               }
    
        return fun
};
/**
 * 从第一个=号位置 分割 string  
 * @param {*} str 
 * @returns 
 */
var splitByEquals=function(str){
     var n=str.indexOf('='),l=str.length;
         if(n<0||n+1===l){ // 没有=号 或 , 只有=号
            return
         }else{
            return [str.slice(0,n).trim(),str.slice(n+1,l).trim()];
         };
};

/**
 * instructs 不同分类的共用方法
 *        适用于 e-input ,e-change, e-html , e-text 单条目指令
 */
var TempFun0=function(fork,kd,methods,arrF,type,attr){
    var dom=kd[0],str=kd[1].trim(),u;
    var i=0,l=0,s='',d,key='',dkr=[],d;
    var fun=null,def;
        if(regFun.test(str)){
            //   str=jxStrFun(str,fork,dkr,arrF);
        }else if(regCs.test(str)&&!regSM2.test(str)){
             //单key
             key=str;  
        };
        str=parser(str,fork,dkr,arrF);
        if(dkr.length===0)dkr[0]=TempGetDefault(fork);  // 没有数据响应的事件 , 存储到 $def
        if(key==''){
             // 单向数据 
             fun=getTypeByFun(type,methods,str,attr);
        }else{
             // key 双向绑定
             fun=getTypeByFun(type,methods,str,attr,key);
        };
        i=0,l=dkr.length;
        while(i<l){
            dkr[i].push([dom,fork,fun]);
          i++;
        };
};

/**
 * 当不存在 对应的key值时 , 添加到default
 * @param {*} fork 
 * @returns 
 */
var TempGetDefault=function(fork){
    var def=fork.$def,u;
        if(def===u){
            def=[]
            fork.$def=def;
        };
        return def;
};

/**
 * 获取 驼峰命名
 * @param {*} str 
 * @returns 
 */
var GetHumpName=function(str){
       if(str.indexOf('-')>-1){
         var d=str.split('-'),i=0,l=d.length,s='',f=0;
         var out='';
             while(i<l){
                s=d[i].trim();
                if(s!==''){
                    if(f===1){
                        out+=s[0].toUpperCase()+s.slice(1,s.length);  
                    }else{
                        out+=s;
                        f=1
                    };
                };
                i++;
             };
             return out;
       };
   return str
};

var atrList=['$attr','$style','$show','$input','$change','$html','$txt','$text','$evt','$filter','$cmt'];
var instructs={

      $evt:function(fork,kd,methods,arrF){
        var dom=kd[0],str=kd[1],u;
        var arr=str.split(';'),i=0,l=arr.length,s='',d,ds='',dkr=[];
        var drr=[],fun=null;
        var p=0,q=0;
            while(i<l){
                s=arr[i].trim();
                if(s!==''){
                    d=splitByEquals(s);
                    if(d!==u){
                            //只支持 调用方法 
                            if(regFun.test(d[1])){
                                d[1]=parser(d[1],fork,dkr,true);
                                drr.push(d);
                            }else{
                                console.error('e-evt  => '+d[1]);
                            };
                    };
                };
                i++;
            };
            i=0,l=drr.length;  
            if(l===0)return;
            // 所有行内事件走 default
            dkr.length=0;  
            dkr[0]=TempGetDefault(fork); // 没有数据响应的事件 , 存储到 $def
            //
            while(i<l){
                d=drr[i]
                fun=getTypeByFun('evt',methods,d[1],d[0]);
                dkr[0].push([dom,fork,fun]);
                // p=0,q=dkr.length;
                // while(p<q){
                    // dkr[p].push([dom,fork,fun])
                //     p++;
                // };
                i++;
            };
      },

      $attr:function(fork,kd,methods,arrF){
        // return;
        var dom=kd[0],str=kd[1],u;
        var arr=str.split(';'),i=0,l=arr.length,s='',d,ds='',dkr;
        var drr=[],fun=null;
        var q=0,p=0;
            // if(arr.length>1)debugger;
            while(i<l){
                s=arr[i].trim();
                if(s!==''){
                    d=splitByEquals(s);
                    if(d!==u){
                            dkr=[];      // 拼接值使用到的所有的 key 存储容器
                            d[2]=dkr;
                            if(regFun.test(d[1])){

                            }else{
                                // 判断是否 纯单key  特例: 用于 checked=key   but ( key!=function && key!='xxx' && key !=abc+'xxx')
                                ds=d[1];
                                if(d[0]==='checked'&&regCs.test(ds)&&!regSM2.test(ds))d[3]=d[1];
                                
                            };
                            d[1]=parser(d[1],fork,dkr);
                            drr.push(d);
                    };
                };
                i++;
            };
            i=0,l=drr.length;  
            if(l===0)return;
            while(i<l){
                d=drr[i]
                fun=getTypeByFun('attr',methods,d[1],d[0],d[3]); // 空 key-evt 存储到default
                dkr=d[2];
                q=0,p=dkr.length;
                if(p===0){
                    p=1; 
                    dkr[0]=TempGetDefault(fork);
                };
                while(q<p){
                    dkr[q].push([dom,fork,fun]);
                    q++;
                };
                i++;
            };
      },

      $style:function(fork,kd,methods,arrF){
        var dom=kd[0],str=kd[1],u;
        var arr=str.split(';'),i=0,l=arr.length,s='',d,ds='',dkr=[];
        var drr=[],fun=null;
        var p=0,q=0;
            while(i<l){
                s=arr[i].trim();
                if(s!==''){
                    d=splitByEquals(s);
                    if(d!==u){
                        
                           d[0]=GetHumpName(d[0]);
                           d[1]=parser(d[1],fork,dkr,arrF);
                           drr.push(d);
                    };
                };
                i++;
            };
            i=0,l=drr.length;  
            if(l===0)return;
            if(dkr.length===0)dkr[0]=TempGetDefault(fork); // 没有数据响应的事件 , 存储到 $def
            //
            while(i<l){
                d=drr[i]
                fun=getTypeByFun('style',methods,d[1],d[0]);
                p=0,q=dkr.length;
                while(p<q){
                    dkr[p].push([dom,fork,fun])
                    p++;
                };
                i++;
            };
      },

      $show:function(fork,kd,methods,arrF){
           TempFun0(fork,kd,methods,arrF,'show',null);
      },

      $input:function(fork,kd,methods,arrF){
           TempFun0(fork,kd,methods,arrF,'input','oninput');
      },

      $change:function(fork,kd,methods,arrF){
           TempFun0(fork,kd,methods,arrF,'input','onchange');
      },

      $html:function(fork,kd,methods,arrF){
           TempFun0(fork,kd,methods,arrF,'html','html');
      },

      $txt:function(fork,kd,methods,arrF){
           TempFun0(fork,kd,methods,arrF,'html','text');
      },

      $text:function(fork,kd,methods,arrF,doms){
          var dom=kd[0],dkr=[],i=0,l=0;
          var str=BraceContentToFunctin(kd[1],fork,dkr);
               if(str==='')return;
          var  fun =getTypeByFun('text' , methods , str) 
                i=0,l=dkr.length;
                while(i<l){
                    dkr[i].push([dom,fork,fun]);
                    i++;
                };
                fun=null;
      },

      $filter:function(fork,kd,methods,arrF){
         var dom=kd[0],str=kd[1].trim(),s=0,e=0,n=0,u;
         var name='',k='',k2='',d=[],dkr=[],krr,dk,fun;
             if(regFun.test(str)){
                n=str.indexOf('(');
                name=str.slice(0,n);  //function name
                if(!(methods.$_filters[name] instanceof Function) ){
                    console.error('missing filter => '+name);
                    return
                };
                k=str.slice(n+1,str.length-1).trim();
                if(k!==''){
                    if(k.indexOf(',')>0){
                        krr=k.split(',');
                        s=0,e=krr.length;
                        while(s<e){
                            k=krr[s].trim();
                            dkr.length=0;
                            k2=parserKey(k,0,fork,dkr);
                            d.push([k,k2,dkr[0]]);    
                            s++;
                        };
                    }else{
                        k2=parserKey(k,0,fork,dkr);
                        d.push([k,k2,dkr[0]]);  
                    };
                }else{
                        d.push(['$fork','$data']);
                };
             }else{
                        console.error('error filter  =>  '+str);
                return
             };
             s=0,e=d.length;
             while(s<e){
                    dk=d[s];  //
                    fun=getFilterFun(methods,name,dk[1], dk[0]);
                    if(dk[2]!==u){  //针对key
                        dk[2].push([dk[0],fork,fun,true]);
                    }else { //  针对 fork    key= '$fork'||'$'  外挂在 $FLT上, 防止失效
                        if(fork.$FLT===u)fork.$FLT=[];
                        fork.$FLT.push([ '$',fork,fun,true])
                    }
                s++;
             };
      },

      $cmt:function(fork,kd,methods,arrF){
        var dom=kd[0],str=kd[1],u;
        var arr=str.split(';'),i=0,l=arr.length,s='',d,ds='',key='',dkr;
        var drr=[],fun=null;
        var O={};
        var mp=null;
            while(i<l){
                s=arr[i].trim();
                if(s!==''){
                    d=splitByEquals(s);
                    if(d!==u){
                        // debugger
                           key=d[0];
                           if(key==='id'){
                                dkr=[];      // 拼接值使用到的所有的 key 存储容器
                                d[2]=dkr;
                                d[1]=parser( d[1] , fork , dkr , regFun.test(d[1]) );
                                drr.push(d);
                                O[key]=d;        
                           }else if(key==='mapping'||key==='data'){
                                ds=d[1];
                                // debugger
                                if(ds[0]==='{'){  // 行内数据 json 
                                    // O[key]=getVfun('{a:123,b:"456"}');
                                    O[key]=getVfun(ds)
                                }else{            // 行内方法    
                                    dkr=[];      // 拼接值使用到的所有的 key 存储容器
                                    d[2]=dkr;
                                    d[1]=parser( d[1] , fork , dkr , regFun.test(d[1]) );
                                    drr.push(d);
                                    O[key]=d;   
                                }
                           }
                    };
                };
                i++
            };
            if(O.id===u)return;
        var res=getComponentFun(O,methods,dom);
        var orr=['id','data','mapping'];
            orr.forEach(function(k){
                      fun=res[k];
                      if(fun){    
                            dkr=O[k][2];
                            i=0,l=dkr.length;
                            if(i===0){
                                l=1; 
                                dkr[0]=TempGetDefault(fork);
                            };
                            while(i<l){
                                dkr[i].push( [ dom , fork , fun ] );
                                i++;
                            };
                     }
            });
            fun=null;
           /*   //id
            dkr=O.id[2];
            i=0,l=dkr.length;
            if(i===0){
                l=1; 
                dkr[0]=TempGetDefault(fork);
            };
            while(i<l){
               dkr[i].push( [ dom , fork , res.id ] );
               i++;
            };
            //data
            if(O.data&&res.data){
                dkr=O.data[2];
                i=0,l=dkr.length;
                if(i===0){
                    l=1; 
                    dkr[0]=TempGetDefault(fork);
                };
                while(i<l){
                   dkr[i].push( [ dom , fork , res.data ] );
                   i++;
                };
            }; */
      }
};

var regFun=/^([A-z]|\_|\$)+\d*\s*\(.*\)$/;              // 以 字母_或$ 开头的 方法   
// var regSm=/([^\?\!\=\:\>\<\+\-\*\/\(\)\%\&]+)/;         // 匹配三目运算符中   不包含 +-*/ ? : > < = () 的参数  ,(没有排除掉 '"abc"' 另外排除)
var regSM2=/(\+|\-|\*|\\|\?|\=|\(|\)|\%|\&|\[|\])+/;    // 匹配是否包含运算符
var regCs=/^([A-z]|\_|\$)+/;                            // 以字母 _ 和 $ 开头的 肯定是 参数名  (  排除掉 '"abc"'  这种 )
var regDh=/,$/;
var regStr=/'|"|`/g;
// var regKh=/\{\{((?:.|\n)+?)\}\}/g;                      // 括号内的内容
// var regSc=/({{|}})/g;                                   // 去除两边的括号
// var regFg=/,(?![^(]*\))/g                               // 以 , 号 和 方法进行分割  ，这个正则表达式的含义是匹配逗号,，但是不匹配后面跟着任意数量（零次或多次）的不包含左括号(的字符，再跟着一个右括号)的子串。这样就可以排除括号内部的逗号。    
// var regStr=/("|'|`)\w+\1/g;                             // 用于将所有的 'xx' , "xx" , `xx` 字符串提取出来   



/**
 * 
 * @param {*} o{ id:xxx, data:xxx} 
 * @returns 
 */
var getComponentFun=function(o,methods,dom){
    var $M=methods,u;
    var idFun=getVfun('function($O,$methods,$evt,$dom){ var $data=$O.$data;  return '+o.id[1]+'}');
    var dFun=null,mFun=null;
    var VT=1,VN=0,init=true;
    var id,data,mapping;    
        
        if(o.data instanceof Array){
            VT++; 
            dFun=getVfun('function($O,$methods,$evt,$dom){ var $data=$O.$data;  return '+o.data[1]+'}');
        }else{
            //行内数据
            data=o.data;
        };
        if(o.mapping instanceof Array){
            VT++; 
            mFun=getVfun('function($O,$methods,$evt,$dom){ var $data=$O.$data;  return '+o.mapping[1]+'}');
        }else{
            //行内数据  
            mapping=o.mapping
        }; 
    var parent=dom;
    var app=null,cmt=null;

    var build=function(){
          if(init){
              if(VN>=VT){  // 组件, 数据 和 映射准备就绪
                   init=false;
                   
              }
          }else{ // 已创建的组件 , 数据更新 

          }
       };
   
        return {
             id:function(dom,$O){
                    if(dom)parent=dom;
                    var v=idFun($O,$M);
                        if(id==v)return;
                        id=v;
                        // 销毁旧的组件 id 发生变化 
                        if(app){
                            app.$_destroy();
                            init=true;
                        };    
                        // 请求新的组件模块
                        Eng.getComponentById(id,function(o){
                              if(o!==null){
                                   cmt=o;
                                   VN++;
                                   build()
                              }
                        });
             },
             data:(dFun?function(dom,$O){
                    if(dom)parent=dom;
                    data=dFun($O,$M);
                    VN++;
                    build()
             }:null),
             mapping:(mFun?function(dom,$O){
                    if(dom)parent=dom;
                    mapping=mFun($O,$M);
                    VN++;
                    build()
             }:null),
        }
   /*  return (function(){
        var $M=methods;
        var fun= getVfun('function($O,$methods,$evt,$dom){ var $data=$O.$data;  return '+str+'}');
            return function(dom,$O){
            //    dom.textContent= fun($O,$M);
            };
    })(); */
};

var getFilterFun=function(methods,fName,str,key){

    return (function(){
        var $M=methods,ft=$M.$_filters,fn=fName,k=key;
        // var fun= getVfun('function($O,$methods,$evt,$dom){ var $data=$O.$data;  return '+str+'}');
        // var F=k==='$_value'||k==='$_index'?true:false; 
        //     return function(dom , $O){
        //           return ft[fn].apply($M,[  F?$O.$N(1) : k,fun($O,$M)])
        //     };  
           var  F=k==='$_value'||k==='$_index'?true:false;
                return function(value , $O){
                    // return ft[fn].apply($M,[k , value]);
                    return ft[fn].apply($M,[  (F?$O.$N(1) :k) , (F?$O.$N(0) : (value==='$'?$O.$data:value))  ]);
               };
     })();
}

/**
 * 对字符串 进行 解析  例: 
 * @param {*}   str =   " abc {{xx}}  "
 *                  ↓↓↓↓↓
 * @param {*}   arr = [' abc ', '{{xx}} ']
 * 
 */
var BraceContentToFunctin=function(str,fork, dkr){
    str=str.trim();
var i=0,l=str.length,s='',u;
var a=[],al=0,n;
var k=[],ds=0,d=[],c;
var p=0,q=0,o=0,vl=0,f=0; 
var strf='';
    /* 
      将 str 中的 {{ }} 内的 部分提取出来  
    */
    while(i<l){
       s=str[i];
       n=BD0[s];
       if(n!==u){
           if(f===1&&n===2){
                a.pop();
                al--;
                // al=0  说明 一个完整闭合的结束 
                if(al===0){
                      f=0;
                      k.push(ds,i+1);
                      ds=i+1; 
                };
           }else if(n===1){
                a.push(n);        
                al++;
                if(al===2){ //两个{{ 开头
                    if(a[0]===a[1]){
                        ds=i-1;
                        f=1;
                    }else{
                        a.length=0;
                        al=0;
                    };
                };
           };
       };
      i++;
    };
    // console.log(k);
    i=0,al=k.length;
    if(l===0)return '';
    while(i<al){
        p=k[i],q=k[i+1];
        if(p>o){
            strf+='"'+str.slice(o,p)+'"+';
        };
        strf+='('+parser(str.slice(p+2,q-2) , fork , dkr)+')';
        //尾部之前才有+
        if(al-i>2)strf+='+';
        o=q;
        i+=2;
    };
    return strf;
};

/**
 *   例: str= ' abc + "haha" + fun() + aaa. '
 * 
 * 
 *   return  function string
 */    
var parser = function( str,fork, dkr , evtF ){
    str=str.trim();
var i=0,l=str.length,s='',u;
var a=[],al=0,n;
var k=[],ds=0,d=[],c;
var p=0,q=0,o=0,vl=0; 
    /* 
      将 str 中的 非 string 部分提取出来  
    */
    while(i<l){
       s=str[i];
       n=BD1[s];
       if(n!==u){
           if(a[al-1]===n){ 
                // 相同的字符串边界的长度 相减
                a.pop();
                al--;
                // al=0  说明 一个闭合的string 结束 
                if(al===0)ds=i+1;   
           }else{
                // 不同的字符串边界的长度 相加
                a.push(n);        
                al++;
                // 字符串边界长度==1时, 且ds!==i 说明  ds到 i 的索引部分是非字符串部分
                if(al===1&&ds!==i)k.push(ds,i);
           };
       };
      i++;
    };
    // 判断漏掉的部分是不是 非字符串部分  (上面的方法,会漏掉结尾的部分)
    if(ds<l)k.push(ds,l);  
    // console.log(k);
    //提取出非字符串的部分并合并
    i=0,al=k.length;
  
 var str2='';   
    while(i<al){
       p=k[i],q=k[i+1];
       // string 部分
       if(p>o){
           s=str.slice(o,p).trim();
           d.push(s);
           str2+='$♋$';
       };
       // key部分
       s=str.slice(p,q).trim();
       str2+=s;
       o=q;
       i+=2;
    }; 
    str2= parser2(str2,d,fork, dkr,evtF);
    //尾部漏掉的 "xxx"
    if(q<l)str2+=str.slice(q,l)
  return str2;

};


/**
 *  提取参数和方法 
 * 
 */ 
var parser2=function(str,strD,fork,dkr,evtF){
        str=str.trim();
    var i=0,l=str.length,s='',u;
    var n=0,ns=0,pn=0;
    var d=[],dl=-1,ds='',dd,pd;
    var f=0;    // f=> 1 : 之前是个key  0:之前是个运算符   f2===2 说明它们之间隔着一个 key
        // console.log(str);
        while(i<l){
           s=str[i];
           n=BD2[s]; 
        //    if(s===']')debugger; 
           //匹配到一个运算符  
           if(n!==u){
               f=0;
               if(ns!==i){
                   //提取非运算符之前的 部分
                   ds=str.slice(ns,i).trim();
                   if(ds!==''){
                        //说明 i 之前的部分可能是合法的 key
                        d.push([ds, 0, ns,i ])       //  *1=> 0:普通key  1:数字 , 2: 方法名  3: data[key]  *2~*3: 值的提取部分
                        dl++;
                        if(ds!=='$♋$'){
                            pd=d[dl];
                        };
                        f=1;
                   };
               };
               if(n===2){ //    方法()
                      if(pd!==u){
                          if(f===1||pn===4){
                                pd[1]=2;    // xxxx() || ['xxx']()
                          };
                      };  
               };
               if(n===3){ // [   data[key] 的方式 
                   if(f===1){
                        if(pd!==u)pd[1]=3;
                   }else{
                        d.push(['$fork',3, i,i ]) 
                        dl++
                   };
               };
               pn=n;
               ns=i+1;
           };
          i++;
        };
        if(ns<l){
              ds=str.slice(ns,l).trim();
              if(ds!==''){
                 d.push([ds, 0, ns, l])
                 dl++;
              };
        };
        i=0,dl=d.length;
        if(dl===0)return str;  //没有参数
        // console.log(d);
    // var kstr='',kn=0,ks=0,ns=0;
    var kstr='',kn=0,ks=0,ns=0,nt=0,pt=0,kk='',k0='';
       
            while(i<dl){
                    dd=d[i];
                    s=dd[0];
                    ks=dd[2];  // 该key 的索引开始
                    nt=dd[1];  // 当前类型 
                    if(ks>ns){
                        kk=str.slice(ns,ks).trim();
                        // kstr+=kk;;
                        if(pt!==2){
                            kstr+=kk;
                        }else{
                            k0=kk[0];
                            if(k0==='('&&kk.length!==1){
                                pt=0
                                kstr+='($evt,$dom'+kk.slice(1).trim();
                            }else if(k0===')'){
                                pt=0;
                                kstr+=',$evt,$dom)'+kk.slice(1).trim();
                            }else{
                                kstr+=kk;
                            };
                        };
                    };
                    if(s==='$♋$'){
                        kstr+=strD[kn];
                        kn++;
                    }else{
                        s=parserKey(s,nt,fork,dkr,evtF);
                        kstr+=s;
                    };
                    if(nt===2)pt=nt;
                    ns=dd[3]; // 该key 的索引结束
                i++;
            };
            if(l>ns){
                kk=str.slice(ns,l).trim();
                if(pt===2){
                    if(kk===')'){
                        kk=',$evt,$dom)'
                    }else{
                        kk='($evt,$dom)'
                    };
                };
                kstr+=kk;
            };  
      return kstr;
};

/**
 * 对parser2 的数据在此解析  
 * 
*/
var parserKey=function(key,type,fork, dkr,evtF){
    var d,u,$='$data',kt='';                           
        if(type===2)return '$methods.'+key;
        if(regCs.test(key)){
            if(key.indexOf('.')<0){ //单值
                  if(key==='true'||key==='false')return key;
                  kt=KT[key];
                  if(kt===$)return $;    // = $data本身 
                    d=fork[key];
                    if(d===u&&evtF!==true){
                        d=[],fork[key]=d;
                        dkr.push(d);
                    }else if(dkr.indexOf(d)<0){ // 每个dom 重复key应该处理一次 
                        dkr.push(d);
                    };
                   return kt===u?'$data.'+key:kt;
            }else{//路径                 
                    var krr=key.split('.'),k='',i=0,l=krr.length,e=l-1,o=fork,c,f=1;
                        while(i<l){
                            k=krr[i].trim();
                            if(i===0){
                                kt=KT[k];
                                if(kt===u){
                                    krr[i]='$O.'+k;
                                }else{
                                    // 行内参数  $.abc 或 $fork.abc 的情况 
                                    f=0;   //结尾不需 不需要拼接 $data. 直接+k
                                    krr[i]=kt;
                                };
                            }; 
                            if(i!==e&&evtF!==true){ 
                                // 非最后一个值前面都是路径
                                d=o[k];
                                if(d===u){
                                    d=getFork();
                                    d.$p=o;
                                    o[k]=d;
                                };
                                o=d;
                            };
                             
                            if(i===e){
                                    //  k
                                    kt=KT[k];
                                    if(kt!==$){
                                        c=o[k];
                                        if(c===u&&evtF!==true){
                                            c=[],o[k]=c;
                                            dkr.push(c)
                                        }if(dkr.indexOf(c)<0){
                                            dkr.push(c)
                                        };
                                        krr[i]=kt===u?(f===1?'$data.'+k:k):kt;
                                }else{
                                        krr[i]=$
                                };
                                
                            }; 
                          i++;
                        };
                        return krr.join('.')
            };
        }else{
               return key;  //数值 或 非法的key
        };     
};
   
/**
 * 获取不同类型对应的处理方法  (基于methods的调用 )
 * @param {*} type     类型
 * @param {*} methods  方法 
 * @param {*} str      拼接数据
 * @param {*} attr     行内属性,事件方法
 * @param {*} key      主要是checked使用的单一 key  
 */
var getTypeByFun=function(type,methods,str,attr,key){
        //有内参  
    //   let fun= getVfun('function($O,$methods){ var $data=$O.$data;  return '+str+'}');
      var m={'checked':1,'disabled':2,'class':3},f,u;  
          switch(type){
               case 'text':
                 return (function(){
                      var $M=methods;
                      var fun= getVfun('function($O,$methods,$evt,$dom){ var $data=$O.$data;  return '+str+'}');
                          return function(dom,$O){
                             dom.textContent= fun($O,$M);
                          };
                 })();
               break;
               case 'attr':
                   f=m[attr];
                   if(f===u){
                            return(function(){
                                        var $M=methods;
                                        var atr=attr;
                                        var fun= getVfun('function($O,$methods,$evt,$dom){ var $data=$O.$data;  return '+str+'}');
                                            return function(dom,$O){
                                                dom.setAttribute(atr,fun($O,$M))
                                            };
                            })();
                   }else if(f===3){  //class;
                        return(function(){
                            var $M=methods;
                            // var atr=attr;
                            var fun= getVfun('function($O,$methods,$evt,$dom){ var $data=$O.$data;  return '+str+'}');
                            var name='',last='',now=''; //
                                return function(dom,$O){
                                    name=dom.className;
                                    now=' '+fun($O,$M);          // 当前的className
                                    name=name.replace(last,now); // 旧的替换为当前的
                                    last=now;                    // 当前变之前
                                    dom.className=name;
                                };
                        })();
                   }else if(f===1&&key!==u){//checked 双向  绑定的单key 非 fun() 或 运算值
                            return(function(){
                                        var $M=methods,u;
                                        var k=key;
                                        var fun= getVfun('function($O,$methods,$evt,$dom){ var $data=$O.$data;  return '+str+'}');
                                        var dm=null,change=null;
                                        var F=true;
                                        var init=function(dom,$O){
                                                    dm=dom;  
                                                var frr=dom.$onchange;
                                                    if(frr==u){
                                                        frr=[];
                                                        dom.$onchange=frr;
                                                    };
                                                    if(!dom.onchange){
                                                        dom.onchange=function(evt){
                                                            var i=0,l=frr.length;
                                                                while(i<l){
                                                                    frr[i](evt,dom)
                                                                    i++;
                                                                };
                                                        };
                                                    };
                                                    dom.$checkedF=function(evt,dom){
                                                        F=false;
                                                        if($O.$data[k]!=u){
                                                            $O.$data[k]=dom.checked;
                                                        }else{
                                                            $O.$data=dom.checked;
                                                        };
                                                        F=true;
                                                    };
                                                    frr.unshift(dom.$checkedF);
                                                    init=null;
                                            };
                                            return function(dom,$O){
                                                    dom.checked=fun($O,$M);
                                                    if(F&&dom.onchange!==null)dom.onchange(null,dom);  //直接修改checked值, 也能触发 onchange事件
                                                    if(dm===null)init(dom,$O); 
                                            };
                            })();
                   }else {//disabled 和 单向 checked 
                           return(function(){
                                    var $M=methods;
                                    var atr=attr;
                                    var fun= getVfun('function($O,$methods){ var $data=$O.$data;  return '+str+'}');
                                        return function(dom,$O){
                                              dom[atr]=fun($O,$M);
                                        };
                          })();
                   };
               break;
               case 'evt':
                    return(function(){
                        var $M=methods,u;
                        var atr=attr;
                        var fun= getVfun('function($O,$methods,$evt,$dom){ var $data=$O.$data;  return '+str+'}');
                        var dm=null;
                        var init=function(dom,$O){
                                dm=dom;  
                                // console.log(str);
                                if(atr==='onchange'||atr==='oninput'){
                                    var fk='$'+atr;    
                                    var frr=dom[fk];
                                        if(frr==u){
                                            frr=[];
                                            dom[fk]=frr;
                                        };
                                        // oninput 和 onchange 事件 ~~~~~
                                        if(!dom[atr]){
                                            dom[atr]=function(evt){
                                                var i=0,l=frr.length;
                                                    while(i<l){
                                                        frr[i](evt,dom)
                                                        i++;
                                                    };
                                            };
                                        };
                                        frr.push(function(evt,dom){
                                            fun($O,$M,evt,dom);
                                        });
                                }else{
                                    dom[atr]=function(evt){
                                         fun($O,$M,evt,dom)
                                    };
                                };
                            };
                            return function(dom,$O){
                                init(dom,$O);
                            };
                    })();
               break;
               case 'input':  //  e-input 和 e-change 
                   if(key!==u){
                         //双向数据
                         return (function(){
                            var $M=methods,u;
                            var k=key,atr=attr;
                            var fun= getVfun('function($O,$methods,$evt,$dom){ var $data=$O.$data;  return '+str+'}');
                            var dm=null;
                            var init=function(dom,$O){
                                       dm=dom;
                                   var fk='$'+atr;    
                                   var frr=dom[fk];
                                        if(frr==u){
                                            frr=[];
                                            dom[fk]=frr;
                                        };
                                        // oninput 和 onchange 事件 ~~~~~
                                        if(!dom[atr]){
                                            dom[atr]=function(evt){
                                                var i=0,l=frr.length;
                                                    while(i<l){
                                                        frr[i](evt,dom)
                                                        i++;
                                                    };
                                            };
                                        };
                                        frr.push(function(evt,dom){
                                            $O.$data[k]=dom.value;
                                        });
                                    
                                    init=null;
                                };
                                return function(dom,$O){
                                        dom.value=fun($O,$M);
                                        if(dm===null)init(dom,$O)
                                };
                         })();
                   }else{
                           //单向数据
                           return (function(){
                                var $M=methods;
                                var fun= getVfun('function($O,$methods,$evt,$dom){ var $data=$O.$data;  return '+str+'}');
                                    return function(dom,$O){
                                         dom.value= fun($O,$M);
                                    };
                           })();
                   };
               break; 
               case 'html':   //  e-html 和 e-txt
                    return (function(){
                        var $M=methods;
                        var atr=attr;
                        var fun= getVfun('function($O,$methods,$evt,$dom){ var $data=$O.$data;  return '+str+'}');
                            if(atr==='text'){
                                    return function(dom,$O){
                                        dom.textContent=fun($O,$M);
                                    };
                            }else{ //html
                                    return function(dom,$O){
                                        dom.innerHTML = fun($O,$M);
                                    };
                            };
                   })();
               break 
               case 'style':
                    return (function(){
                        var $M=methods;
                        var atr=attr;
                        var fun= getVfun('function($O,$methods,$evt,$dom){ var $data=$O.$data;  return '+str+'}');
                            return function(dom,$O){
                                dom.style[atr]= fun($O,$M);
                            };
                    })();
               break;
               case 'show':
                    return (function(){
                        var $M=methods;
                        // var atr=attr;
                        var fun= getVfun('function($O,$methods,$evt,$dom){ var $data=$O.$data;  return '+str+'}');
                            return function(dom,$O){
                                dom.style.display= fun($O,$M)?'':'none';
                            };
                    })();
               break; 
               default:
                console.error('缺少的类型  ~~~~ !!!!!  ');
               break; 
          };
};


// var list=['e-event','e-attr','e-style','e-show','e-if','e-input','e-change','e-html','e-txt','e-fork','e-for','e-stop']; // fordom 排除列表
var LIST=[['$evt','e-evt'],['$attr','e-attr'],['$style','e-style'],['$show','e-show'],['$input','e-input'],['$change','e-change'],['$html','e-html'],['$txt','e-text'],['$filter','e-filter'],['$cmt','e-component'] ],LL=LIST.length;                                   // 

/**
 * 解析 cfg.el 对象 生成基本的初步数据结构
 * @param {*} fork 
 * @param {*} doms 
 * @param {*} tree  fork的根
 * @param {*} id    组件样式id 用于css样式  
 * @param {*} arrF  true: 在循环中, false 不在循环中   
 * @param {*} pfo   父级循环数据 fo对象 ( 用于添加子 fo)
 */
var domLoop=function(fork,doms,tree,methods,id,arrF,pfo){
      let l=doms.length,u;
      let dom=null,attr=null,str='', name='',srr=null,key=null,keyS='', f;
      let cFork=null,s=0,cDoms=null,jxFork=null,pDom;
      let fo=null;     
          while(l--){
                dom=doms[l];
                name=dom.nodeName;
                if(name==='#comment'){                        // 删除注释
                      delDom(dom);
                }else if(name==='#text'){                     // 纯文本 innerText
                      str=dom.textContent;
                      if(str.indexOf('{{')>-1){
                            /* srr=matchKey(str);    // null  || [ 拼接数组, key索引数组  ]
                            if(srr!==null)fork.$text.push([dom,srr]); */

                            fork.$text.push([dom,str]);
                      };
                      
                }else if(dom.getAttribute('e-stop')===null){  // e-stop不再向下执行
                      
                      if(arrF!==true){
                          //非数组 循环才有 $_idDoms
                           attr=dom.getAttribute('e-id');
                           if(attr!==null){
                               dom.removeAttribute('e-id');
                               attr=attr.trim();
                               if(attr!=='')methods.$_idDoms[attr]=dom;
                           };
                      };
                      f=true;
                      // e-for 优先级高于 e-fork  (且不能同时出现)
                      attr=dom.getAttribute('e-for');
                      if(attr!==null){
                                f=false;
                                attr=attr.trim();
                                // console.log(attr)
                                dom.removeAttribute('e-for');
                                // if(attr==='list5')debugger;
                                fo={$c:[]};
                                // 此处应该 删除dom  
                                if(arrF!==true){
                                    cFork=fork[attr]||getFork(fork,tree);
                                }else{
                                    //子循环
                                    cFork=getFork();
                                    cFork.$cFor=true;
                                    pfo.$c.push(fo);
                                };
                                cFork.$p=fork;
                                cFork.$g=tree;
                                cFork.$type='for';
                                fork[attr]=cFork;
                                cFork.$g=tree;
                                cFork.$type='for';
                                pDom=dom.parentNode;
                                
                                domLoop(cFork,[dom],tree ,methods , id, true,fo);
                                forkKeyInit(cFork,methods,true)
                                delDom(dom);
                                forDomInit(dom,cFork,pDom,fo);
                      }else if(arrF!==true){
                            //非循环体 才在此处设置  样式id 
                            if(id!==u)dom.setAttribute('eng_'+id,'');
                      };
                      // e-fork
                      attr=dom.getAttribute('e-fork');
                      if(f&&attr!==null){
                               f=false;
                               attr=attr.trim();
                               dom.removeAttribute('e-fork');
                               cFork=fork[attr]||getFork(fork,tree);
                               cFork.$p=fork;
                               cFork.$g=tree;
                               cFork.$type='base';
                               fork[attr]=cFork;
                               domLoop(cFork , [dom] , tree , methods , id ,arrF,pfo);
                               forkKeyInit(cFork,methods,arrF)
                      };
                      // 普通行内属性 ~~~~~~~~~~~~~~~~~~~
                      if(f){
                          s=0;
                           while(s<LL){
                              key=LIST[s];
                              attr=dom.getAttribute(key[1]);
                              if(attr!==null){
                                  dom.removeAttribute(key[1]);
                                  attr=attr.trim();
                                  if(attr!==''){
                                       fork[key[0]].push([dom , attr]);
                                  };
                              };
                              s++;
                           };
                           cDoms=dom.childNodes;
                           if(cDoms&&cDoms.length>0)domLoop(fork , cDoms , tree , methods , id , arrF , pfo);
                      };
                };
          };
};
  
/**
 *  对初步生成的 操作指令 数据进行 二次处理  
 * @param {*} fork 
 * @param {*} methods 
 * @param {*} arrF 
 */
var forkKeyInit=function(fork,methods,arrF){
     let list=atrList,i=0,l=list.length,kf=null,k='',d,s=0,e=0;
         while(i<l){
            k=list[i]; 
            d=fork[k];
            s=0,e=d.length;
            kf=instructs[k];
            while(s<e){
                kf(fork,d[s],methods,arrF)
              s++;
            };
            d.length=0;   //清空 , 方便插入 分支dom 的镜像数据  (否则分支dom 的镜像数据 会报错 没有数据[]可push数据 )
               

             //临时延迟 删除方案, 最后会统一删除!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
            (function(o,k){
                 setTimeout(function(){
                    delete o[k]
                 })
            })(fork,k);
            // delete fork[k];
            i++;
         };   
};

/**
 * 解析 循环数据 的dom 结构
 * @param {*} dom    
 * @param {*} fork   
 * @param {*} pDom
 * @param {*} fo     {} 存储 $clone,$pDom,$struct 三个构造属性 , 以及 $c:[] 子循环fo数据
 */
var forDomInit=function(dom,fork,pDom,fo){
    var arr=[];
    var struct=forDomStruct(dom,arr);
        fo.$struct=struct;
        fo.$pDom=pDom;
        fork.$JX.push(fo);

        forForkInit(fork,arr,fo);     
};

/**
 * 解析 循环数据的 fork 结构, 用于copy操作  
 * @param {*} fork 
 * @param {*} doms
 * @param {*} fo     储存镜像对象  
 */
var forForkInit=function(fork,doms,fo){
       var clone=getEmpty();
        //    if(fo.$c.length>1)debugger;
          //fo.$c 全部的子镜像数据 , fork.$jx 本身无法收集全部 , 因此使用该值覆盖掉
           forForkStruct(fork,doms,clone,clone,fo.$c);
           fo.$clone=clone;

};

/**
 * 
 * @param {*} fork 
 * @param {*} doms 
 * @param {*} clone
 * @param {*} pClone
 * @param {*} cJX      全部子分支镜像数据 
 */
var forForkStruct=function(fork,doms,clone,pClone,cJX){
      var k='',d,i=0,l=0,c,n=0,vrr,jx,o,u;
    //   debugger
          for(k in fork){
               d=fork[k];
                    if(d instanceof Array){  // key 数据 
                         i=0,l=d.length;
                        
                         while(i<l){
                                c=d[i];
                                n=doms.indexOf(c[0]);
                                if(n>-1){
                                    c[0]=n;  // dom 替换为索引指向
                                }else{
                                     //c[3]=true 是filter 的
                                    // 不应该出现无匹配的情况 !!!!!!!!!!!!
                                    if(c[3]!==true)debugger;  // -2 被标记为了
                                    // debugger
                                };
                            i++;
                         };
                         clone[k]=d;
                         delete fork[k];   //删除 key 数据
                    }else if(d.$type!==u){
                          if(d.$cFor===true){
                                //子循环数据 在 父 fork 上记录 父元素信息 
                                // pClone.$cDom=doms.indexOf(d.$pDom); //  在子元素的父容器索引 保存在 父 fork 下
                                pClone[k]=d;
                                d.$JX=cJX; 
                                jx=cJX,i=0,l=jx.length;
                                while(i<l){
                                     n=doms.indexOf(jx[i].$pDom);
                                     if(n>-1)jx[i].$pDom=n;   // 所有子循环元素的父dom 转换为 索引   (备注: 因为不确定镜像数据的 顺序, 所以需要判断 >-1 )
                                     
                                    i++;
                                };
                                // delete fork[k];
                          }else{ // 下级 base 数据 
                                o=getEmpty();
                                clone[k]=o;
                                delete fork[k];
                                // 向下查找
                                forForkStruct(d,doms,o,pClone,cJX); 
                                o.$type='base';
                          };
                    }else{
                         console.log('不应该被遍历的 key ==>  '+ k );
                    };
          };
};

/**
 * 返回循环dom 的结构体 
 * @param {*} dom 
 * @param {*} arr 
 * @returns {
 *          name:'',     // tagName
 *          txt:'',      // only  #text
 *          attr:[],     // inline attributes 
 *          crr: [],     // childNodes
 * }
 */
var forDomStruct=function(dom,arr){
      var name=dom.nodeName,i=0,l=0,txt='';
          if(name==='#comment')return;
      var cfg=Object.create(null),u;     
          if(name==='#text'){
              txt=dom.textContent.trim();
              if(txt!==''){
                  cfg.txt=txt;
              }else{
                 return
              }
          };
          arr.push(dom);   
          cfg.name=name;
         // 行内属性
      var attrs=dom.attributes,attr=[],a;
          cfg.attr=attr;
          if(attrs){
              i=0,l=attrs.length;
              while(i<l){
                    a=attrs[i];
                    attr.push(a.name,a.value);
                i++;
              };
          };
         // 行内
      var nodes=dom.childNodes,crr=[],rd;
          cfg.crr=crr;
          if(nodes){
              i=0,l=nodes.length;
              while(i<l){
                rd=forDomStruct(nodes[i],arr);
                if(rd!==u)crr.push(rd);
                i++;
              };
          }; 
          return cfg;
};

/**
 * 获得返回数组  $_index 和 $value 的方法 
 * @param {*} data 
 * @param {*} index 
 * @returns 
 */
var getArrVf=function(){
    return (function(){
          var fk=null,d=null,i=0,u;
                return function(type,data,index,fork){
                    // debugger
                        if(type==0){
                            return fk.$data;               // 返回 $_value
                        }else if(type===1){         // 返回 $_index 
                            return i;                
                        }else if(type===2) {       // 基本类型数组的覆盖式修改 或设置 修改index (初始化 和 位置变更时 )
                            d=data;
                            i=index;
                            if(fork!==u)fk=fork;
                            fk.$data=data;
                        }else{
                            i=index;   
                        }; 
                };
    })()
};

/**
 * 创建for元素的  fork 和 dom 初步处理
 * @param {*} fork      父fork
 * @param {*} fo       子fork相关 {$clone,$struct,$pDom,$c:[]}
 * @param {*} doms
 * @param {*} data      数据
 * @param {*} index     数据索引
 * @param {*} real      实际在数据响应中生效的 的 cloneFork
 * @param {*} pJxd      [ ]父元素的镜像 clone 对象 
 */
var forForkBuildInit=function(fork, fo, doms ,data,index,real,pJxd){
    var u,n;
    var arr=[],prr;  //储存数组的 $_index 和 $_value 处理方法
    var getF=null,f=false;
        if(real===u){
            getF=getArrVf();
            f=true;
        }else{
            getF=real.$N;
        };
    
    var clone= forForkBuild( fo.$clone,doms,fork,fork.$g, data , getF , arr,real,pJxd );
        if(f)getF(2,data,index,clone); // 设置 data 和 index    (注意! 设置data主要针对基本数据类型 ,   引用类型也需要传值, 基于代码逻辑 )
        clone.$type='for';
        clone.$pDom=fork.$pDom;
        if(real&&arr.length>0){
            //非index==0 的数据的 $_index 和 $_value 方法,全部推到 index=0的处理队列中 
            prr=real.$ARR;
            if(prr!==u){
                real.$ARR=prr.concat(arr);
            }else{
                real.$ARR=arr;
            };
        }else{
             clone.$ARR=arr.length>0?arr:u;
        };

        // 子循环的父元素  
        // n=fork.$cDom;
        // if(n!==u)clone.$cDom=doms[n];
        return clone;
};

/**
 * 根据循环数据的dom结构 ,创建生成dom
 * @param {*} struct 
 * @param {*} drr  储存所有创建的dom
 * @param {*} id   样式id 若有 
 */
var forDomBuild=function(struct,drr,id){
    let tag=struct.name,u;
    let dom;
    let attr=struct.attr,i=0,l=attr.length;
        if(tag!=='#text'){
            dom=doc.createElement(tag);
            if(id!==u)dom.setAttribute('eng_'+id,'');
        }else{
            dom=doc.createTextNode(struct.txt);
        };
        while(i<l){
              dom.setAttribute(attr[i],attr[i+1]);
              i+=2;
        };
        drr.push(dom)
    let crr=struct.crr,cdom=null;
        i=0,l=crr.length;
        while(i<l){
              cdom=forDomBuild(crr[i],drr);
              dom.appendChild(cdom);
            i++;
        };
        return dom
};

/**
 * 创建循环数据的fork  
 * @param {*} fork 
 * @param {*} doms
 * @param {*} pFork
 * @param {*} tree
 * @param {*} data 
 * @param {*} getF    () 获取数组数据和index以及 设置修改index 的方法 
 * @param {*} arr      储存数据的  $_index 和 $_value 相关处理方法
 * @param {*} real      实际在数据响应中生效的 的 cloneFork
 * @param {*} pJxd    
 */
var forForkBuild=function(clone,doms,pFork,tree,data,getF,arr,real,pJxd){
      var o=getEmpty(),k='',d='',krr,c,i=0,l=0,f=false,co,cReal,jx,jo,dd, u;
          for(k in clone){
              d=clone[k];
              if(d instanceof Array){
                    f=k==='$_index'||k==='$_value';
                    if(real===u){
                        krr=[];
                        o[k]=krr;
                    }else{
                        krr=real[k];
                        if(krr===u){
                            krr=[];
                            real[k]=krr;
                        };
                    };
                    i=0,l=d.length;
                    while(i<l){
                        // c=d[i].slice(0,3);
                        dd=d[i];
                        c=dd.slice(0,dd.length);
                        // c[0]=doms[c[0]];
                        c[0]=doms[c[0]]||c[0];
                        c[1]=o;
                        krr.push(c);
                        if(f)arr.push(c);
                        i++;
                    };

             }else{ //base 路径
                   if(d.$cFor!==true&&real!==u){
                        cReal=real[k];
                        if(cReal===u){
                            //如果生效fork上没有该分支, 则创建
                            cReal=getEmpty();
                            cReal.$p=real;
                            cReal.$g=tree;
                            cReal.$type='base';
                            real[k]=cReal;
                        };
                        forForkBuild(d,doms,o,tree,data&&data[k],getF,arr,cReal,pJxd);
                   }else{ // 子循环 
                        // debugger
                        co=getEmpty();
                        co.$cFor=true;
                        co.$p=o;
                        co.$g=tree;
                        co.$type='for';
                        jx=d.$JX;
                        i=0,l=jx.length;
                        // debugger
                        while(i<l){
                           jo=jx[i]; 
                          /*  co.$JX[i]={
                             $struct:jo.$struct,
                             $clone:jo.$clone,
                             $pDom:doms[jo.$pDom]
                           };  */
                           pJxd.push({
                              $struct:jo.$struct,
                              $clone:jo.$clone,
                              $pDom:doms[jo.$pDom]
                           })
                           i++;
                        };
                        co.$JX=pJxd;
                        o[k]=co;
                     
                        
                   };
             };
          };
        //   if(clone.$cDom!==u){
        //     o.$cDom=doms[clone.$cDom]; //子循环的 父元素 
        //   }; 
          o.$data=data;
          o.$p=pFork;
          o.$g=tree;
          o.$N=getF;
          o.$V=getF;
          o.$type='base';
         return o;
};

/**
 * Loop parsing of data
 * @param {Eng} t           Eng instance
 * @param {JSON} data       user data
 * @param {Array} KRR       当存在时, 不在列表中的数据,会被排除
 * @param {fork} fork   
 * @param {string} path     Splicing Path of Data
 * @param {Boolean} arrF    true: In an array loop    false: Not in an array loop
 * @param {Boolean} indexF  true: The root index loop of an array   false: not
 */
var DataLoop=function(t,data ,KRR ,fork,path,arrF,indexF){   //arrF:处于数组循环中, indexF:数组索引循环层
    let k='',kt=0,d,lx=0,i=0,l=0,o,ck,u;
    let crr=[] ,pathK=''; 
        //
        if(fork!==u)fork.$data=data;
            
        // if(arrF)debugger;
        for(k in data){
             if(KRR===u||KRR.indexOf(k)>-1){
                    d=data[k];
                    lx=typeLX(d);    // 0:disable   1:basic    2: json  3: array
                    kt=keyType(k);  //  0:disable   1:basic    2:不可枚举(但仍然向下响应)
                    if(lx!==0&&kt!==0){
                        pathK=path+k; 
                        if(lx===2){           // json
                                ck=fork&&fork[k]||u;
                                // console.log('path => '+ path);
                                DataLoop(t,d ,KRR, ck, pathK+'.', arrF ,false);
                                t.bindJson(data,k,kt,ck,pathK,arrF,indexF);
                                
                        }else if(lx===3){     // array
                                ck=fork&&fork[k]||u;
                                // 使得支持 响应array 事件
                                ArrEvt(d,t,ck,pathK);
                                t.buildFor(d , ck , pathK);
                                DataLoop(t,d ,KRR, ck , pathK+'.' , true ,true);
                                t.bindArray(data , k , kt , ck , pathK , arrF , false);  
                        }else{
                                crr.push([k,kt,fork,pathK,arrF,indexF]);  // 存储待绑定的的基本数据 , 最后执行
                        };
                    };
             };
        };
        l=crr.length;
        while(i<l){
                o=crr[i];
                t.bindKey(data,o[0],o[1],o[2],o[3],o[4],o[5]);
            i++;
        };
        if(fork!==u)t.defaultKey( fork.$def );
            
};

/**
 * 对私有样式插入约束id 
 * @param {*} str css string
 * @param {*} id  样式id
 * @returns 
 */
var getStyle=function(str,id){
    var reg=/\/\*[\s\S]*?\*\//g;      // 块注释
    // var reg0=/\/\/[\s\S]*(?=\r|\n|$)/g;// 行注释
    var reg1=/(\{[^{}]*\})/g;          // 匹配所有  {xx} 样式属性内容  
    var reg3=/\s+/g;                   // 分割后 所有 id 类名 标签的  大于2的间隔
    var css='',arr,i=0,l,v,sv='',cv='',ar,s=0,e=0,n;
    var pj='[eng_'+id+']';
    //     str=str.replace(reg,'').replace(reg0,'');    // 去除块和行注释 ( 行注释与base64冲突,且标准环境无法使用行注释,故保留)
          str=str.replace(reg,'');    // 去除块注释
          str=str.trim();              // 去除两侧空格s
          css=str.replace(reg3,' ');   // 去除多余空格 (大于2个的空格)   (待匹配修改的纯净 css 文本)
    //     debugger
    //     str=css.replace(reg1,'_-|-_');    // 去除属性内容
    //     arr=str.split('_-|-_');           // 分割所有 id class类 标签属性
        arr=css.split(reg1);   // 分割拼接,所有偶数位的是 id,class或tag
        l=arr.length;
        while(i<l){
              v=arr[i];
              v=v.trim();
              if(v!==''){
                 ar=v.split(reg3);
                 e=ar.length;
                 if(e>1){
                     sv='',s=0;
                     while(s<e){
                        cv=ar[s].trim();
                        if(cv!==''){
                            if(s>0)sv+=' '; 
                            n=cv.search('::');
                            if(n<0){
                                sv+=cv+pj;
                            }else{
                                if(n===0){ 
                                   sv+=pj+cv;
                                }else{
                                   sv+=cv.replace('::',pj+'::');  
                                };
                            };
                        };
                        s++;
                     };
                 }else{
                     n=v.search('::');
                     if(n<0){ 
                         sv= v+pj;
                     }else{
                        sv=v;  
                         if(n===0){ 
                            sv=pj+v;
                         }else{
                            sv=v.replace('::',pj+'::');  
                         };
                     };
                 }; 
                 arr[i]=sv; 
              };
           i+=2;   
       };
    var dom=doc.createElement('style');
        dom.innerText=arr.join('');
        bod.appendChild(dom);
        return dom;   
};
var ID=-1;
var APP=null;

var Eng=function(cfg){
     let t=this;
     let methods= cfg.methods||{};   
         if(cfg.css){
            ID++;
            t.id=ID+'';
            t.style=getStyle(cfg.css,t.id)
         };  
         if(cfg.el.$struct===true){
            t.el=forDomBuild(cfg.el,CACHE,t.id);
            CACHE.length=0;
         }else{
            t.el=cfg.el;
         };
     
         t.methods=methods; 
         t.data=cfg.data||{};
         methods.$_el=t.el;
         methods.$_data=t.data;
         methods.$_filters=cfg.filters||{};
         methods.$_idDoms={};
          
         
         NoDel(methods,'$_el');
         NoDel(methods,'$_data');
         NoDel(methods,'$_filters');
         NoDel(methods,'$_idDoms');
         //
         t.toolsInit(cfg)
         t.elInit(t.el , cfg.$_KRR);
         //
         APP=methods;
         if(cfg.onCreated instanceof Function)cfg.onCreated.apply(methods); 
         return APP;
};

/* Eng.prototype.mappingInit=function(){
     var t=this,M=t.methods;
     var data,map;
          t.setMapData=function(d){
             var o=d.map
                 data=d.data;
                 map={};
          };

          t.loopMap=function(o,m){
              var k='',v,n=0;
              var a;
                  for(k in o){
                     v=o[k];
                     n=typeLX(v);  // 0:disabled 1: basic( string , number , undefind , null ,boolean ) 2: json 3: array
                     if(n===1){
                         m[v]=k;
                     }else if(n===2){
                         if(a.indexOf('=>')>0){
                            a=k.split('=>');   
                            m[v]={};
                            t.loopMap( v ,  )
                         };
                     }else if(n===3){

                     };
                  };
          };
}; */

Eng.prototype.elInit=function(EL,krr){
  var t=this,tree=getFork();
  var id=t.id;
      t.tree=tree; 
      tree.$p=tree;
      tree.$g=tree;
      // tree.$parent=tree,
      // tree.$p=tree,
      // tree.$=tree;
      tree.$type='base';
      domLoop(tree , [EL] ,tree, t.methods ,id, false); //0,1,2,3,4
      console.log(tree);
      forkKeyInit(tree , t.methods ,false);
      
      t.keyBindInit();
      //
      t.forDomInit();
      //
      t.dataInit(t.data,krr);
      //
      
};

Eng.prototype.dataInit=function(DATA,krr){
    var t=this,tree=t.tree,u; 
         
        /**
         *  处理非数据响应的数据, 如 e-event
         * @param {*} d 
         * @returns 
         */
        t.defaultKey=function(d){
            if(d===u)return;
            var i=0,l=d.length,c;
                if(l===0)return;
                while(i<l){
                    c=d[i];
                    c[2](c[0],c[1]);
                    i++;
                }; 
                // d.length=0; //只会执行一次
        };

        DataLoop(t , DATA , krr , tree , '' , false);
        
};

Eng.prototype.keyBindInit=function(){
     var t=this;
         
         /**
          * 过滤其中的 filter  
          * @param {*} krr 
          */
         t.getFilters=function(krr){
            var l=krr.length,d,frr=[];
                while(l--){
                    d=krr[l]
                    if(d[3]===true){     // 第三个参数是true的 为filter
                         krr.splice(l,1);
                         frr.push(d)
                    };
                };
                if(frr.length>0)return frr;
         };
         /**
          * 处理所有key数据 
          * 少儿判断删除的部分 , 每个key的长度 变为0 代表着删除废弃 
          * @param {*} krr 
          */
         t.keyExec=function(krr){
             var i=0,l=krr.length,d;
               while(i<l){
                 d=krr[i];
                 // 少了判断 length长度删除的操作 !!!!!
                 d[2](d[0],d[1]);
                 i++;
               };
         };

         /**
          * 执行filter
          * @param {*} frr 
          * @param {*} v 
          */
         t.filterExec=function(frr,v){
             var i=0,l=frr.length,u;
             var d,ov=v,nv=v;
                 while(i<l){
                    d=frr[i];
                    nv=d[2](nv,d[1]);
                    i++;
                 };
                 if(nv!==u)return nv;
                 return ov; 
         };
         /*
            注意 正常类型 直接 fork[key] 可直接拿到响应数据
            如果是 数组的基本类型数据 ,则需要  fork[key].$ARR;
            indexF=true 时为基本类型数据  
          */ 
         //
         t.bindKey=function(obj,key,kt,fork,path,arrF,indexF){
               var value=obj[key],u;
               var krr, arv,frr,$frr;
                   if(fork!==u){
                      //位于根数据上,指向根数据的filter, 因为无法被遍历,所以需要在此处判断一次,   注意 !!!:  禁止修改返回值 
                      if(fork.$FLT!==u&&fork===fork.$g){
                            $frr=fork.$FLT;
                            fork.$FLT=u;
                            t.filterExec($frr,obj);   //因为是根据数据, 只会被filter 捕获执行1次, 且不允许替换性修改, 
                       };
                       krr=fork[key]||u;              // key 对应的处理出数据
                       if(indexF)arv=fork[key].$ARR;  // 数组索引层对应的 $_value 和$_index的处理数据
                       if(krr!==u){
                          frr=t.getFilters(krr);
                          if(frr!==u){
                             value=t.filterExec(frr,value);
                             obj[key]=value;
                          };   
                          t.keyExec(krr);
                       }; 
                     
                       if(arv!==u)t.keyExec(arv);

                   };
                //    if(indexF)debugger;
                   obk(obj,key,{
                        enumerable:kt===1?true:false,
                        set(v){
                            if(value===v)return;
                            if(frr!==u)v=t.filterExec(frr,v);
                            value=v;
                            // 设置更新 $_value 和 index
                            if(arv!==u){
                                    fork[key].$N(2,v,key<<0);
                                    t.keyExec(arv);
                            };   
                            if(krr!==u)t.keyExec(krr);
                        },
                        get(){
                            return value;
                        }
                   })
         };

         //
         t.bindJson=function(obj,key,kt,fork,path,arrF,indexF){
            var value=obj[key],k='',nv,u;
            var arv,frr;
                if(fork&&indexF){
                        arv=fork.$ARR;     //数组索引层对应的 $_value 和$_index的处理数据
                        frr=fork.$FLT;
                        if(frr!==u){
                            nv=t.filterExec(frr,value);
                            if(nv!==value&&typeLX(nv)===2){
                                for(k in nv){
                                    value[k]=nv[k];  // 从新数据中复制数据 ,保留原始引用数据对象
                                };
                            };
                        };    
                        if(arv!==u)t.keyExec(arv);
                    
                };
                
                obk(obj,key,{
                        enumerable:kt===1?true:false,
                        set(v){
                            if(value===v)return;
                            if(frr!==u)v=t.filterExec(frr,v);
                            if(typeLX(v)===2){
                                for(k in v){
                                    value[k]=v[k];  // 从新数据中复制数据 ,保留原始引用数据对象
                                };
                                if(arv!==u)t.keyExec(arv);
                            }; 
                        },
                        get(){
                            return value;
                        }
                })
         };

         t.bindArray=function(obj,key,kt,fork,path){
            var value=obj[key],k='';
                //
                obk(obj,key,{
                    enumerable:kt===1?true:false,
                    set(v){
                        if(value===v)return;
                        //全新复制 
                        if(typeLX(v)===3){
                            t.buildForNew(value,v);
                        };
                    },
                    get(){
                        return value;
                    }
                })
         };
};

Eng.prototype.forDomInit=function(){
    var t=this,tree=t.tree; 
    var doms=[];      // cache
    var LF=false;     // false: 响应数组的 length变化,  true: 不响应     

        /**
         * 创建 子 fork 和 子dom  (全新的  )
         * @param {*} drr 
         * @param {*} fork 
         * @param {*} pFork 
         */
        t.buildFor=function(drr, fork , path){
            let i=0,l=drr.length,u;
            // console.log(path);
                if(fork===u)return;
                // console.log(path);
            let pDom,d,dom,clone;
            let jx=fork.$JX,s=0,e=jx.length,fo,jxd=[],pfd=[]; // jxd 用于存储 所有子分支数 镜像数据 ,汇聚到 第一个分支(响应数据分支  替换.$JX=jxd ),统一渲染 ,不用判断其它镜像分支
                                                              // pfd 用于储存 第一个真实地址上的 数据响应对象   供镜像分支进行添加删除操作 
            let dmr=[],dm;                                                  
            
                fork.$doms=dmr;

                while(s<e){
                    fo=jx[s];
                    pDom=fo.$pDom;
                    dm=[];      //储存每个 分支镜像的doms 用于增删 排序等操作  每个分支在 独立的 [ ] 内
                    dmr[s]=dm; // 每个分支   
                   
                    i=0;
                    while(i<l){
                        d=drr[i]; 
                        dom=forDomBuild(fo.$struct,doms,t.id);
                        clone=forForkBuildInit(fork, fo ,doms,d,i,pfd[i],jxd);   // s>0?fork[0]:u  镜像fork的key数据 全部追加到 第一个clone 生效对象中
                                                                                              // 对应镜像分支的索引数据, 倒序 !!! 
                        if(s==0){
                            fork[i]=clone;
                            pfd[i]=clone;  // 所有镜像数据的 真实 响应对象 
                        };                            
                        pDom.appendChild(dom);
                        dm[i]=[dom,clone];
                        doms.length=0;
                        i++;
                    };
                    s++;
                };
                // debugger
        };
        
        /**
         * 设置全新的 数据数据
         * @param {*} drr 旧的
         * @param {*} nrr 新的
         */
        t.buildForNew=function(drr,nrr){
            var i=0,l=drr.length,s=nrr.length,e=l;
                if(s<l){
                    e=s;
                    //删除后面的
                    drr.splice(s,l);
                }else if(s>l){
                    e=l;
                    while(l<s){
                        drr.push(nrr[l]); //push 增加的
                        l++;
                    };
                };
                // 已有部分的数据 
                while(i<e){
                    drr[i]=nrr[i];
                    i++;
                };
        };

        /**
         * 创建单条数据的 对应 dom 和fork 并插入正确的位置
         * @param {*} fork    父fork
         * @param {*} data    数据
         * @param {*} index   数据索引
         * @param {*} path    数据路径
         * @param {*} lx      数据类型
         * @param {*} insert  插入对象的index  (在它之前, 若有)
         */
        t.buildForSingle=function(fork,data,index,path,lx,insert){ 
            let u;
            let jx=fork.$JX,s=0,e=jx.length,fo,jxd=[];
            let pDom,d,dom,clone;
            let dmr=fork.$doms,dm,pfd;
            let refer=null;
                if(dmr===u){
                    dmr=[];
                    fork.$doms=dmr;    
                };
                while(s<e){
                        fo=jx[s];
                        pDom=fo.$pDom;
                        dm=dmr[s];
                        if(dm===u){
                                dm=[];
                                dmr[s]=dm;
                        };
                        if(insert!==u){//  指定插入位置 ( 指定indexdom之前)
                                refer=pDom.children[insert]||null;
                        };
                        dom=forDomBuild(fo.$struct,doms);
                        clone=forForkBuildInit(fork , fo , doms , data , index , pfd, jxd );
                        if(s==0){
                                fork[index]=clone;
                                pfd=clone;
                        };    
                        // pDom.appendChild(dom);
                        pDom.insertBefore(dom , refer);  // refer=null 插入最后面
                        if(insert===u){ // 写入最后面  index实际上就是push位置
                                dm[index]=[dom,clone];
                        }else{          // 写入 insert之前 
                                dm.splice(insert,0,[dom,clone]);  //插入指定位置前 
                        };
                        doms.length=0;
                        s++;
                };
                path=path+'.'+index;
                // 数据响应
                if(lx===2){  //json
                    // debugger
                    t.dataLoop(data , fork[index] , path+'.' , true,false);
                    t.bindJson(fork.$data, index , 1 ,fork[index] , path , true ,true);
                    
                }else{ //1;  基本和常量数据类型
                    t.bindKey(fork.$data, index , 1 ,fork , path , true ,true);
                };

        };

        /**
         *  删除指定index 的数据 
         * @param {*} drr    数组数据
         * @param {*} dmr    生成对应的 dom和cloneFork 数组
         * @param {*} fork   父fork
         * @param {*} index  删除的索引
         */
        t.forDel=function(drr,dmr,fork,index){
                delete drr[index];
                dmr.forEach(function(vrr){
                    var d=vrr.splice(index,1)[0];
                        delDom(d[0]);            // 删除创建的dom    
                });
                delete fork[index];
        };

        /**
         * 
         * @param {*} n0 
         * @param {*} n1 
         * @param {*} drr 
         * @param {*} dmr 
         * @param {*} fork 
         * @param {*} path 
         * @param {*} lx 
         */
        t.forSwap=function(n1,n2,drr,dmr,fork,path,lx){
            var d1=drr[n1],d2=drr[n2],u;
                if(d1===u||d2===u||n1===n2)return false;
            var jx=fork.$JX;    
            var fo1=fork[n1],fo2=fork[n2];
                //
                delete drr[n1];
                delete drr[n2];
                drr[n1]=d2;
                drr[n2]=d1;
                //
                fo1.$N(3,0,n2);
                fo2.$N(3,0,n1);
                //
                fork[n2]=fo1;
                fork[n1]=fo2;
                //
                dmr.forEach(function(vrr,i){
                    var pDom=jx[i].$pDom,nodes=pDom.childNodes;
                    var d1=vrr[n1],d2=vrr[n2]
                        vrr[n1]=d2;
                        vrr[n2]=d1;
                        pDom.insertBefore(d2[0],nodes[n1]);
                        pDom.insertBefore(d1[0],nodes[n2]);
                });
                //
                if(lx===2){
                    t.bindJson(fork.$data, n1 , 1 ,fo2 , path+'.'+n1 , true ,true);
                    t.bindJson(fork.$data, n2 , 1 ,fo1 , path+'.'+n2 , true ,true);
                }else{
                    t.bindKey(fork.$data, n1 , 1 ,fork , path+'.'+n1 , true ,true)
                    t.bindKey(fork.$data, n2 , 1 ,fork , path+'.'+n2 , true ,true)
                };
                return true;
        };

        /**
         * 数据整体迁移, 修改索引位置   (注意 . 是否需要 先 执行删除操作,  注意顺序 !!!!!!!!!!!! )
         * @param {*} drr 
         * @param {*} fork 
         * @param {*} path 
         * @param {*} lx
         * @param {*} s    修改的起点  s<e
         * @param {*} e    修改的终点 
         * @param {*} m    修改的方向  偏移的索引方向
         */
        t.forDataMove=function(drr,fork,path,lx,s,e,m){
            var d,fo,n=0;
                if(m>0){       // 向后移动  e= 移动位置+1   移动之后若有数据要先执行删除
                    while(e>s){
                         e--;
                         n=e+m;
                         //
                         fo=fork[e];
                         fork[n]=fo;
                         delete fork[e];
                         //
                         d=drr[e];
                         delete drr[e];  //解除数据响应
                         drr[n]=d;
                        //重新绑定数据 
                        if(lx===2){
                            t.bindJson(fork.$data, n , 1 ,fo , path+'.'+n , true ,true);
                        }else{
                            t.bindKey(fork.$data, n , 1 ,fork , path+'.'+n , true ,true)
                        };
                    };
                }else if(m<0){ // 向前移动  (要先执行删除)
                     while(s<e){
                        n=s+m;
                        //
                        fo=fork[s];
                        fork[n]=fo;
                        fo.$N(3,0,n);   // 修正index
                        delete fork[s];
                        //
                        d=drr[s];
                        delete drr[s];
                        drr[n]=d;
                        //重新绑定数据 
                        if(lx===2){
                            t.bindJson(fork.$data, n , 1 ,fo , path+'.'+n , true ,true);
                        }else{
                            t.bindKey(fork.$data, n , 1 ,fork , path+'.'+n , true ,true)
                        };
                        s++;
                     };
                };
        };

        /**
         * 数组操作事件
         * @param {*} type    ['push':尾部添加,'unshift':头部添加,'pop':尾部删除,'shift':头部删除,'splice','sort','reverse','concat'];
         * @param {*} args 
         * @param {*} drr     数组本身
         * @param {*} fork    fork
         * @param {*} path    path
         * @param {*} drr2    排序后的数组数据 !== drr
         */
        t.forCase=function(type,args,drr, fork,path,drr2){
                if(LF===true)return;                         // 不响应 length 方法 
            let i=0,l=args.length,lx=0,u;
                // if(l>0)lx=typeLX(args[0]);  //0:j禁用  1:常量或基本     2: json  3: array
                // if(lx==0)return;
            let dmr=fork.$doms,n=drr.length,d,s=0,e=0,nl=0;
            let rd;
                if(dmr===u){
                    dmr=[];
                    fork.$doms=dmr;    
                }; 
                // 备注: 删除操作本身是连着镜像一起的     
                switch(type){
                     case 'push': // 尾部添加
                         if(l===0)return;
                         lx=typeLX(args[0]); 
                         if(lx===0)return false;
                         while(i<l){
                            d=args[i];
                            drr[n]=d;
                            t.buildForSingle(fork , d , n , path , lx);
                            i++;
                            n++;
                         };
                          return drr.length;
                     break;
                     case 'unshift': // 头部添加
                          if(l===0)return;
                          lx=typeLX(args[0]); 
                          if(lx===0)return false;
                          //向后移动 所有旧数据 l 个位置
                          t.forDataMove(drr,fork,path,lx,0,n,l);
                          
                          //创建头部新插入的, 倒序插入头部~~~~~~~
                          while(l--){
                                d=args[l];
                                drr[l]=d;
                                t.buildForSingle(fork , d , l , path , lx,0); // 0:每次插入最前面 
                             i++;
                          };
                          return drr.length;
                     break;   
                     case 'pop'://删除尾部的
                          if(n===0)return;
                          rd=drr[n-1];
                          t.forDel(drr,dmr,fork,n-1);      // 删除的 n-1 位
                          LF=true;
                          drr.length=n-1; 
                          LF=false; 
                          return rd;
                     break;   
                     case 'shift'://删除头部
                         if(n===0)return;
                          rd=drr[0];
                          lx=typeLX(rd);
                          // 删除头部 
                          t.forDel(drr,dmr,fork,0);
                          // 向前移动 所有旧数据 1 个位置
                          t.forDataMove(drr,fork,path,lx,1,n,-1);
                          LF=true;
                          drr.length=n-1; 
                          LF=false; 
                          return rd;
                     break;   
                     case 'splice':
                           rd=[];
                           if(l===0||n===0)return rd;
                           s=args[0]<<0;
                        //    if(s===u)return;
                           if(l===1){ // 删除之后所有的 
                                if(s<0)s=n+s;
                                if(s<0)s=0;
                                if(s>n)s=n;
                                e=n;
                                if(e-s===0)return;
                                //倒着删
                                while(s<e){
                                    e--;
                                    rd.unshift(drr[e]);
                                    t.forDel(drr,dmr,fork,e);
                                };
                                LF=true;
                                drr.length=n-(n-s); 
                                LF=false; 
                           }else if(l===2){
                                if(s<0)s=n+s;
                                if(s<0)s=0;
                                if(s>n)s=n;
                                e=args[1]<<0;
                                if(e<=0)return;
                                e=s+e;
                                if(e>n)e=n;
                                if(e-s===0)return;
                                i=e;
                                //倒着删
                                while(s<e){
                                    e--;
                                    rd.unshift(drr[e]);
                                    t.forDel(drr,dmr,fork,e);
                                };
                                if(i<n){
                                     // 向前移动 所有旧数据 n-i 个位置
                                     t.forDataMove(drr,fork,path,lx,i,n, -(i-s));
                                };
                                LF=true;
                                drr.length=n-(i-s); 
                                LF=false; 
                           }else if(l>2){
                                lx=typeLX(args[2]);
                                if(lx===0)return;
                                if(s<0)s=n+s;
                                if(s<0)s=0;
                                if(s>n)s=n;
                                e=args[1]<<0;
                                if(e<0)return;
                                e=s+e;
                                if(e>n)e=n;
                                i=e;
                                //倒着删
                                while(s<e){
                                    e--;
                                    rd.unshift(drr[e]);
                                    t.forDel(drr,dmr,fork,e);
                                };
                                //app.list6.splice(1,3,{txt:'hahaha'})
                                // debugger
                                e=l-2; // 添加的数据个数
                                if(i-s!==e&&i<n){       // 删除!==添加个数    数据长度发生变化 ,  且删除后 留有尾部数据
                                    e=-(i-s)+e;  // 向后或向前移动的索引距离
                                    nl=e;
                                    t.forDataMove(drr,fork,path,lx,i,n, e);
                                };
                                //添加数据
                                i=2,e=l;
                                while (i<l) {
                                    d=args[i];
                                    drr[s]=d;
                                    // debugger
                                    t.buildForSingle(fork , d , s , path , lx,s); // 
                                    i++;
                                    s++;
                                };
                                LF=true;
                                drr.length=n+nl; 
                                LF=false; 
                           };
                           return rd;
                     break;   
                     case 'sort':
                          if(n<2)return;
                          lx=typeLX(drr2[0])
                          t.forSort(drr,drr2,dmr,fork,path,lx);
                          return drr;
                     break;   
                     case 'reverse':
                          if(n<2)return;
                          lx=typeLX(drr2[0])
                          t.forSort(drr,drr2,dmr,fork,path,lx);
                          return drr;
                     break;
                     case 'swap':
                         if(l!==2&&n<2)return false;
                         lx=typeLX(drr[0])
                         s=args[0],e=args[1];
                         return t.forSwap(s,e,drr,dmr,fork,path,lx);
                     break;   
                };

        };

        /**
         * 临时解除数组的所有索引响应
         * @param {*} drr 
         */
        t.forUnBind=function(drr){
            var i=0,l=drr.length;
                while(i<l){
                    delete drr[i];
                    i++;
                };
        };

       /**
        * 对 sort 和 reverse 后的数据重新排序 (包括  dom 和 $doms数据  )
        * @param {*} drr 
        * @param {*} drr2 
        * @param {*} dmr
        * @param {*} fork 
        * @param {*} path 
        * @param {*} lx 
        */
        t.forSort=function(drr,drr2,dmr,fork,path,lx){
              var i=0,l=drr2.length,fo,d,u;
              var nrr=[],n=0;
              var frr=[],s=0,e=dmr.length,dmd,dm;
              var jx=fork.$JX,pDom,nodes;
                  while(i<l){
                        nrr[i]=drr2.indexOf(fork[i].$data)
                        drr[i]=drr2[i];
                     i++;
                  };
                  // 排序fork
                  i=0;
                  while(i<l){
                        n=nrr[i];
                        frr[i]=fork[n]
                    i++;
                  };
                  // fork 重新排
                  i=0;
                  while(i<l){
                      fo=frr[i];
                      fo.$N(3,u,i); //设置新的index
                      fork[i]=fo;
                      i++;
                  };
                  // 排序 镜像 $doms
                  while(s<e){
                        d=[];
                        dmd=dmr[s];
                        i=0;
                        pDom=jx[s].$pDom;  // 倒序
                        nodes=pDom.children;
                        while(i<l){
                                n=nrr[i];
                                dm=dmd[n];
                                d[i]=dm;
                                pDom.insertBefore(dm[0],nodes[i])
                            i++;
                        };
                        dmr[s]=d;
                        s++;
                  };
                  //
                  i=0;
                  while(i<l){
                        if(lx===2){
                            fo=fork[i];
                            t.bindJson(fork.$data, i , 1 ,fo , path+'.'+i , true ,true);
                        }else{
                            t.bindKey(fork.$data, i , 1 , fork , path+'.'+i , true ,true)
                        };
                    i++;
                  };
                //   frr=null,nrr=null;
        };

};

Eng.prototype.toolsInit=function(cfg){
    var t=this,M=t.methods;
    var parent=null,body=doc.body;
        if(cfg.onInit instanceof Function)cfg.onInit.apply(M); 

        M.$_awake=function(p){
            if(p&&p.appendChild instanceof Function)parent=p;
            if(parent===null)return;
            parent.appendChild(t.el);
            M.$_status='active';
        };
        
        M.$_sleep=function(){
                if(M.$_status==='sleep')return;
            var p=t.el.parentNode;
                if(p){
                    parent=p;
                    delDom(t.el);
                };
                M.$_status='sleep';
        };

        M.$_destroy=function(){
             if(cfg.onDestroy)cfg.onDestroy.apply(M);
             var o= M,k='';
             var id=o.$_routerId;
               delDom(t.el);
               for( k in t){
                   t[k]=null;      
               };
               for(k in o){
                   o[k]=null;
               };
               M.$_status='dead';
               console.log('destroy     => '+id);
        };

        /**
         * 设置全新的数据 ,  仅会从新数据中浅复制
         * @param {*} o 
         */
        M.$_setData=function(o){
            var k='';
            var g=t.data;
                for(k in o){
                    g[k]=o[k];
                };
        };

        M.$_status='active';
        NoDel(M,'$_status');
        NoDel(M,'$_awake');
        NoDel(M,'$_sleep');
        NoDel(M,'$_destroy');
};

//#############################################################################################################################

/**
 *  公共组件 
 *  idname:function( $setting )
 */
var COMP=Object.create(null);

var CACHE=[];
var regHttp=/^(http|https):/i;
let regHTML=/\.(html|htm)$/;

/**
 * 获取 dom 的  结构体 
 * @param {*} dom 
 * @returns 
 */
Eng.getDomStrict=function(dom){
    var struct= forDomStruct(dom,CACHE);
        struct.$struct=true;
        CACHE.length=0;
        return struct;
};

let DEV=typeof(Eng_DEV_Tools)!=='undefined'?true:false;

/**
 * 添加组件并设置组件 id
 * @param {*} id 
 * @param {*} path
 * @param {*} callBack
 */
Eng.addComponent=function(id,path,callBack){
    var flag=false,u;
        path=path.trim();
    var cmt=COMP[id]
        if(id&&cmt===u){
              cmt={ok:false};
              COMP[id]=cmt;
        }else if(cmt.path!==u){
            console.warn('The id of the component must be unique!! Duplicate id means that the correct component cannot be obtained through Eng.getComponentById(id,callBack) .  => '+id);
            return
        };
        if(cmt.path===u&&DEV&&regHTML.test(path)){  //   .html文件 , 且是dev模式 , 首先执行html转组件js
                Eng_DEV_Tools.htmlToJs(path,function(flag){
                    console.log(id, flag);
                    if(flag){
                        path=regHttp.test(path)?path:location.origin+path.replace(regHTML,'.js');
                        cmt.ok=true;
                        Eng.addComponent(id,path,callBack);
                    };   
                });
        }else{
                path=regHttp.test(path)?path:location.origin+path.replace(regHTML,'.js');
                cmt.path=path;
                callBack=callBack||cmt.callBack;
                if(callBack){
                        import(path).then(function(module){
                                var def=module.default;
                                    COMP[id].handle=def;
                                    callBack(def);
                                    cmt.callBack=null;
                        }).catch(function(err){
                            callBack(null);
                            cmt.callBack=null;
                            throw err;
                        });
                };
        };
};

NoCfg(Eng,'addComponent');

/**
 * Get the component's build calling method
 * 获取组件的生成调用方法
 * @param {*} path
 * @param {*} callBack 
 */
Eng.getComponentByPath=function(path,callBack){
            path=path.trim();
            path=regHttp.test(path)?path:location.origin+path.replace(regHTML,'.js');
            import(path).then(function(module){
                var def=module.default;
                    cmt.handle=def;
                    callBack(def);
            }).catch(function(err){
                    callBack(null);
                   throw err;
            })
};
/**
 * Get the component's generated calling method by id
 * 通过id获取设置的组件
 * @param {*} id
 * @param {*} callBack
 */
Eng.getComponentById=function(id,callBack){
    let cmt=COMP[id],u;
        if(cmt===u){
            console.error('Unregistered component Id => '+id);
            callBack(null);
            return
        };
        if(cmt.ok===false){
             cmt.callBack=callBack;
            return
        };
        if(cmt.handle!==u){
              callBack(cmt.handle)
        }else{
            import(cmt.path).then(function(module){
                var def=module.default;
                    cmt.handle=def;
                    callBack(def);
            }).catch(function(err){
                    callBack(null);
                   throw err;
            })
        };
};

NoCfg(Eng,'getComponentById');


/**
 * 返回最近一个刚创建的 Eng 实例 (仅能获取一次,之后它将被设置为null)
 * @returns 
 */
Eng.getAPP=function(){
    setTimeout(function(){
        APP=null;
    });
    return APP;
};
NoCfg(Eng,'getAPP');
//
window.Eng=Eng;

})();